//
// Created by Tony on 2024/7/2.
//

#include "ClassParser.h"
#include "../common/KeyWords.h"
#include "../common/Utils.h"
#include "ast/Ast.h"
#include "ast/list/clazz/ClassBody.h"
#include "ast/list/clazz/ClassDefine.h"
#include "ast/list/clazz/ClassModule.h"
#include "ast/list/clazz/FieldDefine.h"
#include "ast/list/clazz/MethodDefine.h"
#include "ast/list/clazz/NativeMethodDecl.h"
#include "ast/list/view_build/AttributeSetter.h"
#include "ast/list/view_build/AttributeSetterList.h"
#include "ast/list/view_build/ViewBuilder.h"
#include "ast/list/view_build/ViewBuilderList.h"
#include <cstring>
#include <memory>
#include <vector>

namespace Parser {

using std::make_shared;

std::shared_ptr<Ast> ClassParser::parse() { return classModule(); }

/**
 * field_define := access_flags name ( (":" type_tag) | ( "=" expr) )";"
 */
std::shared_ptr<FieldDefine> ClassParser::fieldDefine() {
    auto accessFlags = this->accessFlags();
    CHECK_NULL_RETURN(accessFlags, nullptr);

    auto name = this->name();
    CHECK_NULL_RETURN(name, nullptr);

    auto ret = skipPunct(":");
    CHECK_FALSE_RETURN(ret, nullptr);

    auto typeTag = this->typeTag();
    CHECK_NULL_RETURN(typeTag, nullptr);

    auto initExpr = this->expr();
    CHECK_NULL_RETURN(initExpr, nullptr);

    ret = skipPunct(";");
    CHECK_FALSE_RETURN(ret, nullptr);

    return make_shared<FieldDefine>(vector<shared_ptr<Ast>>{accessFlags, name, typeTag, initExpr});
}

/**
 * method_define := access_flags name
 */
std::shared_ptr<MethodDefine> ClassParser::methodDefine() {
    auto accessFlags = this->accessFlags();
    CHECK_NULL_RETURN(accessFlags, nullptr);

    auto name = this->name();
    CHECK_NULL_RETURN(name, nullptr);

    auto parameterList = this->parameterList();
    CHECK_NULL_RETURN(parameterList, nullptr);

    auto returnTypeTag = this->typeTag();
    CHECK_NULL_RETURN(returnTypeTag, nullptr);

    auto methodBody = this->block();
    CHECK_NULL_RETURN(methodBody, nullptr);

    return make_shared<MethodDefine>(
        vector<shared_ptr<Ast>>{accessFlags, name, parameterList, returnTypeTag, methodBody});
}

/**
 * class_body := "{" { (field_define | method_define) } "}"
 */
std::shared_ptr<ClassBody> ClassParser::classBody() {
    auto ret = skipPunct("{");
    CHECK_FALSE_RETURN(ret, nullptr);

    vector<shared_ptr<Ast>> members;

    while (true) {
        if (isPunct(tokenQueue.peek(), "}")) {
            tokenQueue.poll();
            break;
        }

        auto accessFlags = this->accessFlags();
        CHECK_NULL_RETURN(accessFlags, nullptr);

        auto name = this->name();
        CHECK_NULL_RETURN(name, nullptr);

        auto token = tokenQueue.peek();
        if (isPunct(token, ":")) { // field
            auto typeTag = this->typeTag();
            CHECK_NULL_RETURN(typeTag, nullptr);

            ret = skipPunct("=");
            CHECK_FALSE_RETURN(ret, nullptr);

            auto initExpr = this->expr();
            CHECK_NULL_RETURN(initExpr, nullptr);

            ret = skipPunct(";");
            CHECK_FALSE_RETURN(ret, nullptr);

            auto fieldDefine = make_shared<FieldDefine>(
                vector<shared_ptr<Ast>>{accessFlags, name, typeTag, initExpr});
            members.push_back(fieldDefine);
        } else if (isPunct(token, "(")) { // method
            auto parameterList = this->parameterList();
            CHECK_NULL_RETURN(parameterList, nullptr);

            auto returnTypeTag = this->typeTag();
            CHECK_NULL_RETURN(returnTypeTag, nullptr);

            if (isPunct(tokenQueue.peek(), ";")) { // native method declare
                tokenQueue.poll();
                auto nativeMethodDecl = make_shared<NativeMethodDecl>(
                    vector<shared_ptr<Ast>>{accessFlags, name, parameterList, returnTypeTag});
                members.push_back(nativeMethodDecl);
                continue;
            }

            shared_ptr<Ast> methodBody;
            if (strcmp(name->name().c_str(), "build") == 0) {
                methodBody = viewBuilderBlock();
            } else {
                methodBody = block();
            }
            CHECK_NULL_RETURN(methodBody, nullptr);

            auto methodDefine = make_shared<MethodDefine>(vector<shared_ptr<Ast>>{
                accessFlags, name, parameterList, returnTypeTag, methodBody});
            members.push_back(methodDefine);
        } else {
            setErrMsg("error syntax at %s", token->getPosition().startStr().c_str());
            return nullptr;
        }
    }

    return make_shared<ClassBody>(members);
}

/**
 * class_define := access_flags "class" name class_body
 */
std::shared_ptr<ClassDefine> ClassParser::classDefine() {
    auto accessFlags = this->accessFlags();
    CHECK_NULL_RETURN(accessFlags, nullptr);

    auto ret = skipName(KeyWords::CLASS);
    CHECK_FALSE_RETURN(ret, nullptr);

    auto className = this->name();
    CHECK_NULL_RETURN(className, nullptr);

    auto classBody = this->classBody();
    CHECK_NULL_RETURN(classBody, nullptr);

    return make_shared<ClassDefine>(vector<shared_ptr<Ast>>{accessFlags, className, classBody});
}

/**
 * class_module := { import_stmt } { class_define }
 */
shared_ptr<ClassModule> ClassParser::classModule() {
    auto classModule = make_shared<ClassModule>();

    while (true) {
        auto token = tokenQueue.peek();
        if (token->type() == Lexer::TokenType::EOF_T) {
            break;
        }
        if (isName(token, KeyWords::IMPORT)) {
            auto importStmt = this->importStmt();
            classModule->addImport(importStmt);
        } else {
            auto classDefine = this->classDefine();
            CHECK_NULL_RETURN(classDefine, nullptr);
            classModule->addClassDefine(classDefine);
        }
    }

    return classModule;
}

/**
 * attribute_setter_list := { "." name argument_list }
 */
shared_ptr<AttributeSetterList> ClassParser::attributeSetterList() {
    vector<shared_ptr<Ast>> attributeSetters;
    while (true) {
        if (!isPunct(tokenQueue.peek(), ".")) {
            break;
        }
        tokenQueue.poll();

        auto name = this->name();
        CHECK_NULL_RETURN(name, nullptr);

        auto argumentList = this->argumentList();
        CHECK_NULL_RETURN(argumentList, nullptr);

        auto attributeSetter =
            make_shared<AttributeSetter>(vector<shared_ptr<Ast>>{name, argumentList});
        attributeSetters.push_back(attributeSetter);
    }

    return make_shared<AttributeSetterList>(attributeSetters);
}

/**
 * view_builder_list := { "{" name argument_list view_builder_list attribute_setter_list ";" "}" }
 */
shared_ptr<ViewBuilderList> ClassParser::viewBuilderList() {
    auto ret = skipPunct("{");
    CHECK_FALSE_RETURN(ret, nullptr);

    vector<shared_ptr<Ast>> viewBuilders;
    while (true) {
        if (isPunct(tokenQueue.peek(), "}")) {
            tokenQueue.poll();
            break;
        }

        auto name = this->name();
        CHECK_NULL_RETURN(name, nullptr);

        auto argumentList = this->argumentList();
        CHECK_NULL_RETURN(argumentList, nullptr);

        shared_ptr<ViewBuilderList> viewBuilderList = nullptr;
        if (isPunct(tokenQueue.peek(), "{")) {
            viewBuilderList = this->viewBuilderList();
            CHECK_NULL_RETURN(viewBuilderList, nullptr);
        }

        auto attributeSetterList = this->attributeSetterList();
        CHECK_NULL_RETURN(attributeSetterList, nullptr);

        auto viewBuilder = make_shared<ViewBuilder>(
            vector<shared_ptr<Ast>>{name, argumentList, viewBuilderList, attributeSetterList});
        viewBuilders.push_back(viewBuilder);
    }

    return make_shared<ViewBuilderList>(viewBuilders);
}

shared_ptr<ViewBuilderBlock> ClassParser::viewBuilderBlock() {
    auto viewBuilderList = this->viewBuilderList();
    CHECK_NULL_RETURN(viewBuilderList, nullptr);

    auto viewBuilder = viewBuilderList->getViewBuilder(0);
    return make_shared<ViewBuilderBlock>(vector<shared_ptr<Ast>>{viewBuilder});
}

} // namespace Parser
